// SPDX-FileCopyrightText: 2023-2025 Sangfor Technologies Inc.
// SPDX-License-Identifier: Mulan PSL v2
package com.geniusai.aip.auth.service.impl;

import com.geniusai.aip.auth.config.ExternalConfig;
import com.geniusai.aip.auth.service.ProfileService;
import com.geniusai.aip.auth.domain.Profile;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
import io.fabric8.kubernetes.api.model.ObjectReference;
import io.fabric8.kubernetes.api.model.ObjectReferenceBuilder;
import io.fabric8.kubernetes.api.model.Secret;
import io.fabric8.kubernetes.api.model.SecretBuilder;
import io.fabric8.kubernetes.api.model.ServiceAccount;
import io.fabric8.kubernetes.api.model.ServiceAccountBuilder;

import io.fabric8.kubernetes.api.model.rbac.PolicyRuleBuilder;
import io.fabric8.kubernetes.api.model.rbac.Role;
import io.fabric8.kubernetes.api.model.rbac.RoleBinding;
import io.fabric8.kubernetes.api.model.rbac.RoleBindingBuilder;
import io.fabric8.kubernetes.api.model.rbac.RoleBuilder;
import io.fabric8.kubernetes.api.model.rbac.RoleRefBuilder;
import io.fabric8.kubernetes.api.model.rbac.SubjectBuilder;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * @author hml14563
 * @date 2021/5/6
 */
@Service
@Slf4j
public class ProfileServiceImpl implements ProfileService {

    @Autowired
    private KubernetesClient client;

    @Autowired
    private CustomResourceDefinitionContext profileContext;

    @Autowired
    private ExternalConfig externalConfig;

    @Override
    public void createProfile(Profile profile) throws IOException {
        client.customResource(profileContext).create(profile.getProfile());
    }

    @Override
    public void deleteProfile(String username) throws IOException {
        client.customResource(profileContext).delete(null, username);
    }

    @Override
    public void updateProfile(Profile profile) throws IOException {
        client.customResource(profileContext).edit(null, profile.getUsername(), profile.getProfile());
    }

    @Override
    public void createSecret(String secretName, String username, String accesskey, String secretKey) {
        Map<String, String> annotation = new HashMap<>(4);
        annotation.put("serving.kubeflow.org/s3-endpoint", externalConfig.getMinioService() + ":" + externalConfig.getMinioServicePort());
        annotation.put("serving.kubeflow.org/s3-usehttps", "0");
        ObjectMeta meta = new ObjectMetaBuilder().withName(secretName).withAnnotations(annotation).withNamespace(username).build();
        Map<String, String> data = new HashMap<>(4);
        data.put("AWS_ACCESS_KEY_ID", accesskey);
        data.put("AWS_SECRET_ACCESS_KEY", secretKey);
        Secret secret = new SecretBuilder().withApiVersion("v1").withMetadata(meta).withType("Opaque").withData(data).build();
        client.secrets().inNamespace(username).create(secret);
    }

    @Override
    public void createMysqlSecret(String secretName, String username) {
        HashMap<String, String> secretData = new HashMap(8);
        secretData.put("DB_AIP", Base64.getEncoder().encodeToString(externalConfig.getAipDatabase().getBytes()));
        secretData.put("DB_HOST", Base64.getEncoder().encodeToString(externalConfig.getDbHost().getBytes()));
        secretData.put("DB_PASSWORD", Base64.getEncoder().encodeToString(externalConfig.getDbPassword().getBytes()));
        secretData.put("DB_TRAINING_MANAGE", Base64.getEncoder().encodeToString(externalConfig.getTrainingDatabase().getBytes()));
        secretData.put("DB_USERNAME", Base64.getEncoder().encodeToString(externalConfig.getDbUsername().getBytes()));
        secretData.put("DB_WEB_DATABASE", Base64.getEncoder().encodeToString(externalConfig.getWebDatabase().getBytes()));
        secretData.put("PORT", Base64.getEncoder().encodeToString(externalConfig.getDbPort().getBytes()));
        ObjectMeta meta = new ObjectMetaBuilder().withName(secretName).withNamespace(username).build();
        Secret dbSecret = new SecretBuilder().withApiVersion("v1").withMetadata(meta).withType("Opaque").withData(secretData).build();
        client.secrets().inNamespace(username).create(dbSecret);

    }

    @Override
    public void createUserRole(String roleName, String username) {
        Role role = new RoleBuilder().withNewMetadata().withName(roleName).withNamespace(username).endMetadata()
                .addToRules(0, new PolicyRuleBuilder().addToApiGroups(0, "argoproj.io")
                        .addToResources(0, "pods").addToResources(1, "workflows").addToVerbs(0, "*").build()
                )
                .addToRules(1, new PolicyRuleBuilder().addToApiGroups(0, "")
                        .addToResources(0, "persistentvolumes").addToResources(1, "persistentvolumeclaims")
                        .addToVerbs(0, "*").build()
                )
                .addToRules(2, new PolicyRuleBuilder().addToApiGroups(0, "snapshot.storage.k8s.io")
                        .addToResources(0, "volumesnapshots").addToVerbs(0, "*").build()
                )
                .addToRules(3, new PolicyRuleBuilder().addToApiGroups(0, "apps").addToApiGroups(1, "")
                        .addToApiGroups(2, "extensions").addToResources(0, "deployments").addToResources(1, "replicasets")
                        .addToVerbs(0, "*").build()
                )
                .addToRules(4, new PolicyRuleBuilder().addToApiGroups(0, "batch")
                        .addToResources(0, "jobs").addToVerbs(0, "*").build()
                )
                .addToRules(5, new PolicyRuleBuilder().addToApiGroups(0, "kubeflow.org")
                        .addToResources(0, "*").addToVerbs(0, "*").build()
                )
                .addToRules(6, new PolicyRuleBuilder().addToApiGroups(0, "machinelearning.seldon.io")
                        .addToResources(0, "seldondeployments").addToVerbs(0, "*").build()
                )
                .addToRules(7, new PolicyRuleBuilder().addToApiGroups(0, "")
                        .addToResources(0, "pods").addToResources(1, "persistentvolumeclaims")
                        .addToResources(2, "secrets").addToVerbs(0, "get")
                        .addToVerbs(1, "list").addToVerbs(2, "watch").build()
                )
                .addToRules(8, new PolicyRuleBuilder().addToApiGroups(0, "")
                        .addToResources(0, "configmaps").addToResources(1, "pods/exec")
                        .addToResources(2, "pods/log").addToResources(3, "services")
                        .addToVerbs(0, "*").build()
                )
                .addToRules(9, new PolicyRuleBuilder().addToApiGroups(0, "serving.kubeflow.org")
                        .addToResources(0, "inferenceservices")
                        .addToVerbs(0, "get").addToVerbs(1, "create").addToVerbs(2, "list").build()
                )
                .build();
        client.rbac().roles().inNamespace(username).create(role);

        // 创建角色绑定信息
        log.info("开始创建rolebinding的用户:{}", username);
        createUserRoleBinding(roleName, username);
        log.info("成功创建rolebinding的用户:{}", username);
    }

    /**
     * 用户创建时添加kale与kubeflow通信的Role RoleBinding信息
     *
     * @param roleName 角色名称
     * @param username namespace of the user
     */
    private void createUserRoleBinding(String roleName, String username) {
        String roleBindingName = roleName + "Binding-default";
        // 绑定ServiceAccount账号default
        RoleBinding kubernetesRoleBinding = new RoleBindingBuilder()
                .withNewMetadata()
                .withName(roleBindingName)
                .withNamespace(username)
                .endMetadata()
                .addToSubjects(
                        0, new SubjectBuilder()
                                .withKind("ServiceAccount")
                                .withName("default")
                                .withNamespace(username)
                                .build()
                )
                .withRoleRef(
                        new RoleRefBuilder()
                                .withApiGroup("rbac.authorization.k8s.io")
                                .withKind("Role")
                                .withName(roleName)
                                .build()
                )
                .build();
        client.rbac().roleBindings().inNamespace(username).create(kubernetesRoleBinding);

        // 绑定ServiceAccount账号default-editor
        String roleBindingName1 = roleName + "Binding-default-editor";
        RoleBinding kubernetesRoleBinding1 = new RoleBindingBuilder()
                .withNewMetadata()
                .withName(roleBindingName1)
                .withNamespace(username)
                .endMetadata()
                .addToSubjects(
                        0, new SubjectBuilder()
                                .withKind("ServiceAccount")
                                .withName("default-editor")
                                .withNamespace(username)
                                .build()
                )
                .withRoleRef(
                        new RoleRefBuilder()
                                .withApiGroup("rbac.authorization.k8s.io")
                                .withKind("Role")
                                .withName(roleName)
                                .build()
                ).build();
        client.rbac().roleBindings().inNamespace(username).create(kubernetesRoleBinding1);
    }

    @Override
    public void createServiceAccount(String saName, String username, String secretName) {
        ObjectMeta saMeta = new ObjectMetaBuilder().withName(saName).withNamespace(username).build();
        ObjectReference secretRef = new ObjectReferenceBuilder().withName(secretName).build();
        ServiceAccount serviceAccount = new ServiceAccountBuilder().withApiVersion("v1").withMetadata(saMeta).withSecrets(secretRef).build();
        client.serviceAccounts().inNamespace(username).create(serviceAccount);
    }
}
